package cn.wolfcode.shop.web.interceptor;

import cn.wolfcode.shop.domain.User;
import cn.wolfcode.shop.redis.AccessLimitKeyPrefix;
import cn.wolfcode.shop.redis.RedisServer;
import cn.wolfcode.shop.redis.UserKeyPrefix;
import cn.wolfcode.shop.result.CodeMsg;
import cn.wolfcode.shop.service.ISeckillUserService;
import cn.wolfcode.shop.web.result.Result;
import cn.wolfcode.shop.web.util.AccessLimit;
import cn.wolfcode.shop.web.util.CookieUtil;
import cn.wolfcode.shop.web.util.UserContext;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class AccessLimitInterceptor extends HandlerInterceptorAdapter {
    @Reference
    private ISeckillUserService seckillUserService;
    @Autowired
    private RedisServer redisServer;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class);
            User user = getUser(request, response);
            UserContext.set(user);
            //访问的方法接口,作为key的组成部分,避免不同方法接口之间访问互不影响
            String methodName = handlerMethod.getMethod().getName();
            if(accessLimit!=null){//如果贴了AccessLimit注解
                int seconds = accessLimit.seconds();//注解限制的时间范围
                int maxCount = accessLimit.maxCount();//注解限制时间范围内的访问次数
                //注解上是否需要登录的标记
                boolean needLogin = accessLimit.needLogin();
                if(needLogin){
                    if(user==null){
                        renderResponse(response,Result.error(CodeMsg.NEED_LOGIN));
                        return false;
                    }
                }
                //来到这里说明 要么不需要登录,要么已经登录了
                if(seconds > 0 && maxCount > 0) {
                    String keyPrefix = null;
                    if(user!=null){
                        //如果登录了,根据用户id限制访问次数  用 用户id作为key的主要部分,每访问一次value+1,并且在第一次访问的时候设置key的值,和失效时间
                        keyPrefix = "USERID:"+user.getId()+":"+methodName;
                    }else{
                        keyPrefix = "IP:"+request.getRemoteAddr().replace(".","")+":"+methodName;
                    }
                    AccessLimitKeyPrefix key = new AccessLimitKeyPrefix(keyPrefix,seconds);
                    Long count;
                    //当前限制的请求路径
                    String uri = request.getRequestURI();
                    if(redisServer.exists(key,"")){
                        count = redisServer.incr(key, uri);
                    }else{
                        count = redisServer.incr(key, uri);
                        redisServer.expire(key,"",seconds);
                    }
                    if(count>maxCount){
                        renderResponse(response,Result.error(CodeMsg.ACCESS_LIMIT));
                        return false;
                    }

                }
            }

        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        UserContext.remove();
    }

    public User getUser(HttpServletRequest request,HttpServletResponse response) throws Exception{
        String token = CookieUtil.getCookieValue(request, UserKeyPrefix.USER_TOKEN_COOKIE_NAME);
        if(token==null){
            return null;
        }
        //当token存在的时候则表示,当前登录用户仍处于活跃状态发起请求,在访问我们的应用,
        // 则对应的应该将他的cookie和存在redis中的凭证重置有效时间
        User user = seckillUserService.getByToken(token);
        if(user == null){
            return null;
        }
        //并且重置用户浏览器的cookie的有效时间
        CookieUtil.addCookie(response,UserKeyPrefix.USER_TOKEN_COOKIE_NAME,token,UserKeyPrefix.USER_TOKEN.getExpireSeconds());
        return user;
    }
    public void renderResponse(HttpServletResponse response, Result error) throws IOException {
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(error));
    }
}
